package com.smart.common.utils.ffmpeg;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import com.smart.common.config.FfmpegConfig;
import com.smart.common.utils.file.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class FfmpegUtils {
    @Autowired
    private FfmpegConfig ffmpegConfig;

    private final Map<String, Map<String, Process>> processMap = new ConcurrentHashMap<>();
    private final Map<String, Map<String, AtomicInteger>> counterMap = new ConcurrentHashMap<>();
    private final ExecutorService executorService = Executors.newFixedThreadPool(10); // 创建一个固定大小的线程池
    private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5); // 定时任务线程池

    @PostConstruct
    public void init() {
        // 删除资源
        FileUtils.deleteDirectoryAllFiles(new File(getOutputPath()));
        // 启动定时任务检查计数器
        scheduledExecutorService.scheduleAtFixedRate(this::checkCounters, 30, 60, TimeUnit.SECONDS);
    }

    /**
     * 销毁残留资源
     */
    @PreDestroy
    public void shutdown() {
        for (String bucketId : processMap.keySet()) {
            Map<String, Process> channelProcesses = processMap.get(bucketId);
            for (String channel : channelProcesses.keySet()) {
                // 关闭所有进程
                Process process = channelProcesses.get(channel);
                if (process != null) {
                    process.destroy();
                }
                // 删除所有生成的文件
                FileUtils.deleteDirectoryAllFiles(new File(getOutputPath(bucketId, channel)));
            }
            processMap.remove(bucketId);
        }
        executorService.shutdown();
        scheduledExecutorService.shutdown();
    }

    /**
     * 启动FFmpeg进程
     *
     * @param bucketId 进程标识符
     * @param channel  通道标识符
     */
    public void startProcess(String bucketId, String channel) {
        String[] command = getCommand(bucketId, channel);
        System.err.println("执行指令:");
        for (String s : command) {
            System.err.print(s + " ");
        }
        System.err.println();

        ProcessBuilder builder = new ProcessBuilder(command);
        try {
            Process process = builder.start();
            processMap.computeIfAbsent(bucketId, k -> new ConcurrentHashMap<>()).put(channel, process);
            counterMap.computeIfAbsent(bucketId, k -> new ConcurrentHashMap<>()).put(channel, new AtomicInteger(0));

            // TODO 仅测试环境使用
            // 捕获标准输出
            executorService.submit(() -> readStream(process.getInputStream(), bucketId, channel));
            // 捕获标准错误
            executorService.submit(() -> readStream(process.getErrorStream(), bucketId, channel));
            System.err.println(process.getInputStream() == process.getErrorStream());

        } catch (IOException e) {
            throw new RuntimeException("启动FFmpeg进程失败!", e);
        }
    }

    private void readStream(InputStream inputStream, String bucketId, String channel) {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
            String line;
            while ((line = reader.readLine()) != null) {
                handleLine(line, bucketId, channel);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void handleLine(String line, String bucketId, String channel) {
        // 处理标准输出的内容
//        System.out.println("垃圾站:" + bucketId + ",通道:" + channel + ",OUT: " + line);
//        System.out.flush();
        // 解析 frame 值并更新计数器
        Map<String, String> dataMap = formatStringToMap(line);
        String frameValue = dataMap.get("frame");
        if (frameValue != null) {
            try {
                int frame = Integer.parseInt(frameValue);
                counterMap.get(bucketId).get(channel).set(frame);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 停止指定标识符的FFmpeg进程
     *
     * @param bucketId 进程标识符
     * @param channel  通道标识符
     */
    public void stopProcess(String bucketId, String channel) {
        Map<String, Process> channelProcesses = processMap.get(bucketId);
        if (channelProcesses != null) {
            Process process = channelProcesses.get(channel);
            if (process != null) {
                process.destroy();
                channelProcesses.remove(channel);
                System.out.println("已关闭FFmpeg进程：" + bucketId + ", 通道：" + channel);
            } else {
                System.out.println("未找到指定的FFmpeg进程：" + bucketId + ", 通道：" + channel);
            }
            // 删除所有生成的文件
            FileUtils.deleteDirectoryAllFiles(new File(getOutputPath(bucketId, channel)));
            // 如果没有更多的通道进程，移除整个bucketId
            if (channelProcesses.isEmpty()) {
                processMap.remove(bucketId);
                counterMap.remove(bucketId);
            }
        } else {
            System.out.println("未找到指定的FFmpeg进程：" + bucketId + ", 通道：" + channel);
        }
    }

    private void checkCounters() {
        long currentTime = System.currentTimeMillis();
        for (String bucketId : counterMap.keySet()) {
            Map<String, AtomicInteger> channelCounters = counterMap.get(bucketId);
            for (String channel : channelCounters.keySet()) {
                AtomicInteger counter = channelCounters.get(channel);
                int currentCount = counter.get();
                // 假设如果计数器在10秒内没有变化，则认为线程已经死亡
                if (currentTime - lastCheckedTime.getOrDefault(bucketId + channel, 0L) > 10000) {
                    if (lastCount.getOrDefault(bucketId + channel, 0) == currentCount) {
                        System.out.println("计数器未变化，终止进程：" + bucketId + ", 通道：" + channel);
                        stopProcess(bucketId, channel);
                    } else {
                        lastCount.put(bucketId + channel, currentCount);
                        lastCheckedTime.put(bucketId + channel, currentTime);
                    }
                }
            }
        }
    }

    private final Map<String, Integer> lastCount = new ConcurrentHashMap<>();
    private final Map<String, Long> lastCheckedTime = new ConcurrentHashMap<>();

    /**
     * 格式化字符串,并转为Map
     *
     * @param str
     * @return
     */
    private Map<String, String> formatStringToMap(String str) {
        Pattern pattern = Pattern.compile("(frame|fps|q|size|time|bitrate|speed)\\s*=\\s*([^\\s,]+)");
        Matcher matcher = pattern.matcher(str);

        // 创建一个 Map 来存储解析后的数据
        Map<String, String> dataMap = new HashMap<>();

        // 查找匹配项并存储到 Map 中
        while (matcher.find()) {
            String key = matcher.group(1);
            String value = matcher.group(2);
            dataMap.put(key, value);
        }
        return dataMap;
    }
//rtsp://admin:smart2024@192.168.88.118:554/cam/realmonitor?channel=3&subtype=1
    public String[] getCommand(String ip, String port,String name, String username, String password, String url, String saveDirName) {
        String[] command = new String[]{
                name+"",
                "-rtsp_transport","tcp",
                "-i", "rtsp://" + username + ":" + password + "@" + ip + ":" + port + url,  // 输入流的RTSP URL
                "-c:v", "libx264",
                "-c:a", "aac",
                "-hls_time", "10",
                "-hls_list_size", "2",
                "-hls_flags", "delete_segments",
                "-start_number", "0",
                "-f", "hls",
                saveDirName + "/output.m3u8"  // 输出HLS的.m3u8 playlist文件和ts分片
        };
        return command;
    }

    public String getOutputPath(String bucket, String channel) {
        String path = null;
        if (System.getProperty("os.name").toLowerCase().contains("win")) {
            path = ffmpegConfig.getWindowsUrl() + bucket + "/" + channel;
        } else {
            path = ffmpegConfig.getLinuxUrl() + bucket + "/" + channel; // 替换为实际的Linux路径
        }
        File file = new File(path);
        if (!file.exists()) {
            file.mkdirs();
        }
        return path;
    }

    public String getOutputPath(String bucket) {
        String path = null;
        if (System.getProperty("os.name").toLowerCase().contains("win")) {
            path = ffmpegConfig.getWindowsUrl() + bucket;
        } else {
            path = ffmpegConfig.getLinuxUrl() + bucket; // 替换为实际的Linux路径
        }
        File file = new File(path);
        if (!file.exists()) {
            file.mkdirs();
        }
        return path;
    }

    public String getOutputPath() {
        String path = null;
        if (System.getProperty("os.name").toLowerCase().contains("win")) {
            path = ffmpegConfig.getWindowsUrl();
        } else {
            path = ffmpegConfig.getLinuxUrl(); // 替换为实际的Linux路径
        }
        File file = new File(path);
        if (!file.exists()) {
            file.mkdirs();
        }
        return path;
    }

    /**
     * 获取命令
     *
     * @param bucketId 垃圾站id
     * @param channel  通道id
     * @param subType  码流0主码流1辅码流
     * @return
     */
    public String[] getCommand(String bucketId, String channel, String subType) {
        return getCommand(ffmpegConfig.getIp(),
                ffmpegConfig.getPort(),
                ffmpegConfig.getName(),
                ffmpegConfig.getUsername(),
                ffmpegConfig.getPassword(),
                ffmpegConfig.getUrl() + "?channel=" + channel + "&subtype=" + subType,
                getOutputPath(bucketId, channel)
        );
    }

    public String[] getCommand(String bucketId, String channel) {
        // 默认使用辅码流
        String subType = "1";
        return getCommand(ffmpegConfig.getIp(),
                ffmpegConfig.getPort(),
                ffmpegConfig.getName(),
                ffmpegConfig.getUsername(),
                ffmpegConfig.getPassword(),
                ffmpegConfig.getUrl() + "?channel=" + channel + "&subtype=" + subType,
                getOutputPath(bucketId, channel)
        );
    }
}
